using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Fated.Character.ChartGeneration;
using Fated.SaveEngine;

namespace Fated.Character {
    public abstract class AbstractCharacter : IJSON {
        #region Member Keys
        protected const string AvailClassesKey = "availclasses";
        protected const string CurClassKey = "curclass";
        protected const string ExpKey = "exp";
        protected const string UnspentExpKey = "expunspent";
        protected const string CharChartKey = "charchart";
        protected const string InventoryKey = "inventory";
        protected const string CurMagicKey = "curmagic";
        protected const string CurHealthKey = "curhealth";
        protected const string CharNameKey = "charname";
        protected const string PositionKey = "position";
        #endregion

        protected Dictionary<string, object> data = new Dictionary<string, object>();

        public AbstractCharacter() {
            AvailClasses = new PreviousClasses();
            AvailClasses.AddClass("Peasant");
            CurrentClassName = "Peasant";
            CharChart = new Chart();
        }
        public AbstractCharacter(Dictionary<string, object> input) {
            setData(input);
        }

        public virtual void setData(Dictionary<string, object> input) {
            data = input;
            try {
                Dictionary<string, object> chartInfo = (Dictionary<string, object>)data[CharChartKey];
                CharChart = new Chart(chartInfo);
                Dictionary<string, object> equipInfo = (Dictionary<string, object>)data[InventoryKey];
                Inventory = new Equipment(equipInfo);
                Dictionary<string, object> classInfo = (Dictionary<string, object>)data[AvailClassesKey];
                AvailClasses = new PreviousClasses(classInfo);
            } catch (InvalidCastException e) {

            }
        }

        public void addClass(string name) {
            AvailClasses.AddClass(name);
        }
        public void removeClass(string name) {
            if (CurrentClassName.CompareTo(name) == 0) {
                CurrentClassName = "Peasant";
            }
            AvailClasses.removeClass(name);
        }
        public void setClass(string name) {
            CurrentClassName = name;
        }

        public void addItem(string item, int amount) {
            Inventory.addItem(item, amount);
        }
        public void removeItem(string item, int amount) {
            Inventory.removeItem(item, amount);
        }

        public float X { 
            get {
                if (data.ContainsKey(PositionKey))
                    return ((Vector3)data[PositionKey]).X;
                else
                    data.Add(PositionKey, Vector3.Zero);
                return 0.0f;
            }
            set {
                if (data.ContainsKey(PositionKey)) {
                    Vector3 temp = ((Vector3)data[PositionKey]);
                    temp.X = value;
                    data[PositionKey] = temp;
                } else
                    data.Add(PositionKey, new Vector3(value, 0, 0));
            }
        }
        public float Y {
            get {
                if (data.ContainsKey(PositionKey))
                    return ((Vector3)data[PositionKey]).Y;
                else
                    data.Add(PositionKey, Vector3.Zero);
                return 0.0f;
            }
            set {
                if (data.ContainsKey(PositionKey)) {
                    Vector3 temp = ((Vector3)data[PositionKey]);
                    temp.Y = value;
                    data[PositionKey] = temp;
                } else
                    data.Add(PositionKey, new Vector3(0, value, 0));
            }
        }
        public float Z {
            get {
                if (data.ContainsKey(PositionKey))
                    return ((Vector3)data[PositionKey]).Z;
                else
                    data.Add(PositionKey, Vector3.Zero);
                return 0.0f;
            }
            set {
                if (data.ContainsKey(PositionKey)) {
                    Vector3 temp = ((Vector3)data[PositionKey]);
                    temp.Z = value;
                    data[PositionKey] = temp;
                } else
                    data.Add(PositionKey, new Vector3(0, 0, value));
            }
        }
        public Point GridPosition {
            get { return new Point((int)X, (int)Y); }
        }

        public string Name {
            get {
                if (data.ContainsKey(CharNameKey))
                    return (string)data[CharNameKey];
                else
                    data.Add(CharNameKey, "Blankyblank");
                return (string)data[CharNameKey];
            }
            set {
                if (data.ContainsKey(CharNameKey))
                    data[CharNameKey] = value;
                else
                    data.Add(CharNameKey, value);
            }
        }

        public int UnspentExperience {
            get {
                if (data.ContainsKey(UnspentExpKey))
                    return (int)data[UnspentExpKey];
                else
                    data.Add(UnspentExpKey, 0);
                return 0;
            }
            set {
                if (data.ContainsKey(UnspentExpKey))
                    data[UnspentExpKey] = value;
                else
                    data.Add(UnspentExpKey, value);
            }
        }
        public int Experience {
            get {
                if (data.ContainsKey(ExpKey))
                    return (int)data[ExpKey];
                data.Add(ExpKey, 0);
                return 0;
            }
            set {
                if (data.ContainsKey(ExpKey))
                    data[ExpKey] = value;
                else
                    data.Add(ExpKey, value);
            }
        }

        internal PreviousClasses AvailClasses {
            get {
                if (data.ContainsKey(AvailClassesKey))
                    return (PreviousClasses)data[AvailClassesKey];
                throw new Exception("Available Classes Key Not Found");
            }
            set {
                if (data.ContainsKey(AvailClassesKey))
                    data[AvailClassesKey] = value;
                else
                    data.Add(AvailClassesKey, value);
            }
        }
        public string CurrentClassName {
            get {
                if (data.ContainsKey(CurClassKey))
                    return (string)data[CurClassKey];
                throw new Exception("Current Class Key Not Found");
            }
            set {
                if (!AvailClasses.containsClass(value)) {
                    throw new Exception("Class not available. Typo?");
                }
                if (data.ContainsKey(CurClassKey))
                    data[CurClassKey] = value;
                else
                    data.Add(CurClassKey, value);
            }
        }
        private CharacterClass CurrentClass {
            get {
                return AvailClasses.getClass(CurrentClassName);
            }
        }

        internal Chart CharChart {
            get {
                if (data.ContainsKey(CharChartKey))
                    return (Chart)data[CharChartKey];

                throw new Exception("Character Chart Key Not Found");
            }
            set {
                if (data.ContainsKey(CharChartKey))
                    data[CharChartKey] = value;
                else
                    data.Add(CharChartKey, value);
            }
        }
        public DateTime Birthdate {
            get { return CharChart.getBirthdate(); }
            set { CharChart.setBirthdate(value); }
        }

        internal Equipment Inventory {
            get {
                if (data.ContainsKey(InventoryKey))
                    return (Equipment)data[InventoryKey];

                throw new Exception("Inventory Key Not Found");
            }
            set {
                if (data.ContainsKey(InventoryKey))
                    data[InventoryKey] = value;
                else
                    data.Add(InventoryKey, value);
            }
        }

        public int CurrentMagic {
            get {
                if (data.ContainsKey(CurMagicKey))
                    return (int)data[CurMagicKey];

                throw new Exception("Inventory Key Not Found");
            }
            set {
                if (data.ContainsKey(CurMagicKey))
                    data[CurMagicKey] = value;
                else
                    data.Add(CurMagicKey, value);
            }
        }
        public int CurrentHealth {
            get {
                if (data.ContainsKey(CurHealthKey))
                    return (int)data[CurHealthKey];

                throw new Exception("Current Health Key Not Found");
            }
            set {
                if (data.ContainsKey(CurHealthKey))
                    data[CurHealthKey] = value;
                else
                    data.Add(CurHealthKey, value);
            }
        }
        public int MaxHealth {
            get { return (int)(CharChart.Health * CurrentClass.Health) + AvailClasses.bonusHealth(); }
        }
        public int MaxMagic {
            get { return (int)(CharChart.Magic * CurrentClass.Magic) + AvailClasses.bonusMagic(); }
        }
        public int Body {
            get { return (int)(CharChart.Body * CurrentClass.Body) + AvailClasses.bonusBody(); }
        }
        public int Mind {
            get { return (int)(CharChart.Mind * CurrentClass.Mind) + AvailClasses.bonusMind(); }
        }
        public int Spirit {
            get { return (int)(CharChart.Spirit * CurrentClass.Spirit) + AvailClasses.bonusSpirit(); }
        }
        public int Social {
            get { return (int)(CharChart.Social * CurrentClass.Social) + AvailClasses.bonusSocial(); }
        }
        public int Speed {
            get { return (int)((CharChart.Speed * CurrentClass.Speed) + AvailClasses.bonusSpeed()) / 10 + 1; }
        }
        public int MoveRange {
            get { return Speed; }
        }
        public int AttackRange {
            get {
                if (Inventory.RHand != null) {
                    return Inventory.RHand.Range;
                } else if (Inventory.LHand != null) {
                    return Inventory.LHand.Range;
                } else
                    return 1; 
            }
        }

        public Ability getAbility(string aName) {
            List<Ability> moveset = ClassMngr.getMoveset(CurrentClass.Name);
            foreach (Ability a in moveset){
                if (a.name == aName) {
                    return a;
                }
            }
            return null;
        }
        public List<string> Abilities {
            get { return CurrentClass.Abilities; }
        }

        public Dictionary<string, object> ToJSON() {
            return data;
        }
    }
}
